/**
* @Author: zou.yingbin
* @Create  : 2020/9/10 11:41
* @Modify  : 2020/9/10 11:41
 */

package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
	"time"
)

type stQuoteEx struct {
	BidPrice  float64
	AskPrice  float64
	LastPrice float64
	Tm        string
}

// 行情连接信息
type QuoteSession struct {
	conn       net.Conn
	loginId    uint64
	token      string
	bConnected bool
	bRun       bool
	recvCount  uint64
	mQuote     map[string]stQuoteEx
	c          chan []byte
}

// 订阅行情任务
func (r *QuoteSession) Do(loginId uint64, token string) {
	var err error
	r.conn, err = net.Dial("tcp", Stcfg.QuoteAddress)
	if err != nil {
		fmt.Println(err)
		return
	}

	if Stcfg.PrintLog != 0 {
		fmt.Println(loginId, "connect quote server success")
	}

	r.loginId = loginId
	r.token = token
	r.bConnected = true
	r.bRun = true
	r.recvCount = 0
	r.c = make(chan []byte, 100)
	r.mQuote = make(map[string]stQuoteEx)

	go r.onMessage(r.c)
	go r.doRecv()
	go r.subscribeQuote()
	go r.sendHeart()
}

func (r *QuoteSession) send(bigType byte, smallType uint16, serialNum uint32, msg []byte) {
	sendBuf := MakeQuotePacket(bigType, smallType, serialNum, msg)
	_, err := r.conn.Write(sendBuf)
	if err != nil {
		fmt.Println("quote send packet fail:", err)
	}

	// bigType=18 为心跳包, 不打印心跳包
	if Stcfg.PrintLog != 0 && bigType != 18 {
		log.Printf("%v send bigType[%d] smallType[%d] serial[%d] len[%d]\n",
			r.loginId, bigType, smallType, serialNum, len(sendBuf))
	}
}

func (r *QuoteSession) GetField(body []byte, flag uint32) (string, error) {
	for i := 0; i < len(body); i++ {
		if uint32(body[i]) == flag {
			str := make([]byte, 0, 10)
			for j := i + 2; j < len(body); j++ {
				if body[j] != 0x01 {
					str = append(str, body[j])
					continue
				}

				return string(str), nil
			}
			break
		}
	}

	return "", fmt.Errorf("not found field, flag:%d", flag)
}

func (r *QuoteSession) onMessage(msg chan []byte) {
	for buf := range msg {
		if len(buf) < 2 {
			return
		}

		goodsCode, _ := r.GetField(buf, 0x21)
		askPrice, _ := r.GetField(buf, 0x4c)
		bidPrice, _ := r.GetField(buf, 0x42)
		lastPrice, _ := r.GetField(buf, 0x24)
		t, _ := r.GetField(buf, 0x41)

		f := func(str string) float64 {
			if d, err := strconv.ParseFloat(str, 10); err == nil {
				return d
			}

			return 0
		}

		r.mQuote[goodsCode] = stQuoteEx{
			LastPrice: f(lastPrice),
			BidPrice:  f(bidPrice),
			AskPrice:  f(askPrice),
			Tm:        t,
		}
		//log.Printf("recv %+v %+v %+v %+v %+v\n", goodsCode, askPrice, bidPrice, lastPrice, t)
	}
}

func (r *QuoteSession) doRecv() {
	defer r.conn.Close()
	defer func() {
		r.bConnected = false
	}()

	for r.bRun {
		var err error
		var nRead int = 0
		headerBuf := make([]byte, 14)
		nRead, err = io.ReadFull(r.conn, headerBuf)
		if err != nil || nRead != 14 {
			fmt.Println(r.loginId, ": read quote header, conn maybe closed")
			return
		}

		//fmt.Println("read head:", headerBuf)

		p := &MiQuotePacket{}
		err = p.UnPackHead(headerBuf)
		if err != nil {
			fmt.Println("parse quote header fail", err)
			return
		}

		if p.Length > 1024*1024*10 || p.Length < 15 {
			fmt.Println("quote packet too big or len error, len:", p.Length)
			return
		}

		dataBuf := make([]byte, p.Length-14)
		nRead, err = io.ReadFull(r.conn, dataBuf)
		if err != nil || nRead != len(dataBuf) {
			fmt.Println(r.loginId, ": read data error:", err)
			return
		}

		if p.Length == 15 {
			//fmt.Println("heart packet.")
			continue
		}

		if Stcfg.PrintLog != 0 {
			// 65 = 行情, 每收到100个打印1次日志
			if p.BigType == 65 {
				if r.recvCount++; r.recvCount%100 == 0 {
					log.Printf("%v recv quote, total:%d\n", r.loginId, r.recvCount)
				}
			} else {
				log.Printf("%v recv bigType[%d] smallType[%d] serial[%d] iLen:%d\n",
					r.loginId, p.BigType, p.SmallType, p.SerialNum, p.Length)
			}
		}

		if p.BigType == 65 {
			r.c <- dataBuf
		}
	}
}

func (r *QuoteSession) subscribeQuote() {
	time.Sleep(time.Second * 1)
	msg := make([]byte, 0)
	msg = append(msg, Uint64ToBytesBigEnd(r.loginId)...)
	token := make([]byte, 64)
	copy(token, []byte(r.token))
	msg = append(msg, token...)
	var nGoodsCount uint32 = uint32(len(QuoteGoods))
	msg = append(msg, UintTobytesBigEnd(nGoodsCount)...)

	for i := 0; i < int(nGoodsCount); i++ {
		msg = append(msg, byte(0))
		msg = append(msg, byte(250))
		goodscode := make([]byte, 64)
		copy(goodscode, []byte(QuoteGoods[i]))
		msg = append(msg, goodscode...)
	}

	r.send(0x20, 0, GetSerialID(), msg)
}

func (r *QuoteSession) sendHeart() {
	for r.bConnected && r.bRun {
		msg := make([]byte, 0)
		r.send(0x12, 0, 0, msg)
		time.Sleep(time.Second * 30)
	}
}

func (r *QuoteSession) Stop() {
	r.bRun = false
	if r.bConnected {
		r.conn.Close()
	}
}
